<?php

/**
 * Gestionnaire d'applicatif
 *
 * @category   McCube
 * @package    Bundle
 * @author     Leiha Sellier <leiha.sellier@gmail.com>
 * @license    http://creativecommons.org/licenses/by/3.0/  CC BY 3.0
 * @link       http://code.google.com/p/mechanics-cubes/
 */
class McCube_Bundle{

    protected static $_repository = array();

    /** */
    public static function getBundle($absoluteName){
        return self::$_repository[$absoluteName];
    }

    // --------------------------------------------------

    public $name         = '';
    public $absoluteName = '';
    public $rootPath     = '';
    public $parentBundle = null;
    public $subPaths     = array(
        'library', 'models', 'forms', 'configs', 'views', 'controllers', 'plugins', 'entities', 'cache', 'public'
    );

    /** */
    public function __construct($name, $rootPath, $parentBundle=null){
        $this->name         = $name;
        $this->absoluteName = $parentBundle->absoluteName.'/'.$name;
        self::$_repository[$this->absoluteName] = $this;

        $this->rootPath     = $rootPath;
        $this->parentBundle = $parentBundle;
        $this->subPaths     = $this->buildPaths($rootPath, $this->subPaths);
    }

    /** */
    public function register($type, $name, $object){
         self::$_repository[$this->absoluteName.'/'.$type][$name] = $object;
         return $object;
    }

    /** */
    public function get($get, $bundle='', $args=array()){
        if(is_array($get))
            return $this->getByArray($get, $bundle, $args);

        return $this->getByString($get, $bundle, $args);
    }

    /** */
    public function getByString($get, $bundle='', $args=array()){
        $pattern =
             '(?:p:(<plugin>.[^/]+){0,1}'
            .'(?:e:(<entity>.[^/]+){0,1}'
            .'(?:m:(<model>.[^/]+){0,1}'
            .'(?:f:(<form>.[^/]+){0,1}'
            .'(?:v:(<view>.[^/]+){0,1}'
            .'(?:c:(<controller>.[^/]+){0,1}'
            .'(?:a:(<action>.[^/]+){0,1}'
        ;
        preg_match('~'.$pattern.'~', $get, $matches);
        return $this->getByArray($matches, $bundle, $args);
    }

    /** */
    public function getByArray($get, $bundle='', $args=array()){
        if(!$bundle)
            $bundle = $this->absoluteName;

        $ret = self::$_repository[$bundle];
        if(isset($get['plugin']))
            $ret = $ret->getPlugin($get['plugin']);

        if(isset($get['entity'])){
            $ret = $ret->getEntity($get['entity']);
        }
        elseif(isset($get['model'])){
            $ret = $ret->getModel($get['model']);
        }
        elseif(isset($get['form'])){
            $ret = $ret->getForm($get['form']);
        }
        elseif(isset($get['view'])){
            $ret = $ret->getView($get['view']);
        }
        elseif(isset($get['controller'])){
            $ret = $ret->getController($get['controller']);

            if(isset($get['action']))
                $ret = $ret->runAction($get['action'], $args);
        }
        return $ret;
    }

    /** */
    public function buildPaths($rootPath, $subPaths){
        foreach($subPaths as $subPath)
            $subPaths[$subPath] = $rootPath.$subPath.DIRECTORY_SEPARATOR;

        return $subPaths;
    }

    /** */
    public function getPath($type){
        return $this->subPaths[$type];
    }

    /** */
    public function isPath($path, $required=true){
        if(is_dir($path))
            return true;

        if($required)
            throw new McCube_Bundle_Exception('Directory', 404, array(
                 'bundle' => $this->absoluteName
                ,'path'   => $path
            ));
    }

    /** */
    public function isFile($file, $required=true, $type='File'){
        if(is_file($file))
            return $file;

        if($required)
            throw new McCube_Bundle_Exception($type, 404, array(
                 'bundle' => $this->absoluteName
                ,'file'   => $file
            ));
    }

    /** */
    public function isFileClass($file, $classname, $required=true){
        if($this->isFile($file, false)){
            include_once($file);
            if(!class_exists($classname, false)){
                throw new McCube_Bundle_Exception('Class', 404, array(
                     'bundle' => $this->absoluteName
                    ,'file'   => $file
                    ,'name'   => $classname
                ));
            }
            return true;
        }

        if($required)
            throw new McCube_Bundle_Exception('File:Class', 404, array(
                 'bundle' => $this->absoluteName
                ,'file'   => $file
                ,'name'   => $classname
            ));
    }

    /** */
    public function getFilePath($type, $name, $ext='php'){
        return $this->subPaths[$type].$name.'.'.$ext;
    }

    /** */
    public function getFileArray($type, $file, $required=true){
        $file = $this->getPath($type).$file;

        if($this->isFile($file, $required)){
            return include($file);
        }
    }

    /** */
    public function getFileContent($type, $file, $required=true){
        if($this->isFile($file, $required))
            return file_get_contents(
                $this->getPath($type).$filename
            );
    }

    /** */
    public function putFileContent($type, $filename, $content){
        $basedir = $this->getPath($type).dirname($filename);
        if(!is_dir($basedir))
            mkdir($basedir,'0777', true);

        file_put_contents($file, trim($content));
    }

    /** */
    public function getConfig($name, $required=true, $default=array(), $extension='.php'
        , $type='configs', $longName='Config', $errorMsg='Config'
    ){
        if($config = $this->getFileArray($type, $name.$extension, $required))
            return $config;

        if($config = $this->getFileArray($type, $name.$longName.$extension, $required))
            return $config;

        if($required)
            throw new McCube_Bundle_Exception($errorMsg, 404, array(
                 'bundle' => $this->absoluteName
                ,'file'   => $file
                ,'name'   => $name
            ));

        return $default;
    }

    /** */
    public function getConfigEntity($name, $required=true, $default=array(), $extension='.php'){
        return $this->getConfig($name, $required, $default, $extension
            , 'entities', 'Entity', 'Config:Entity'
        );
    }

    /** */
    public function getConfigForm($name, $required=true, $default=array(), $extension='.php'){
        return $this->getConfig($name, $required, $default, $extension
            , 'forms', 'Form', 'Config:Form'
        );
    }


    /** */
    public function getEntity($name){
        $file      = $this->subPaths['entities'].$name;
        $classname = $name.'Entity';
        if($this->isFileClass($file, $classname, false)
            || $this->isFileClass($file.'Entity', $classname, false)){
            $object = new $classname();
        }
        elseif($config = $this->getConfigEntity($name)){
            $object = $this->database->getTable($name, $config);
        }

        return $this->register('entity', $name, $object);
    }

    /** */
    public function getModel($name){
        $name      = ucFirst($name);
        $file      = $this->subPaths['models'].$name;
        $classname = $name.'Model';
        if(!$this->isFileClass($file.'.php', $classname, false)
            && !$this->isFileClass($file.'Model.php', $classname, false)
        ){
            throw new McCube_Bundle_Exception('File:Model', 404, array(
                 'bundle' => $this->absoluteName
                ,'path'   => $file
                ,'name'   => $name
            ));
        }
        return $this->register('model'
            , $name
            , new $classname($name, $this)
        );
    }

    /** */
    public function getTplEngine($type=null){
        switch($type){
            case 'form':$class = 'McCube_Template_Form';break;
            default    :$class = 'McCube_Template';break;
        }
        return new $class($this);
    }

    /** */
    public function getView($name){
        return $this->getTplEngine()->fetch($name);
    }

    /** */
    public function getPathFormView($name, $required=true, $cache=false, $extension='.tpl'){
        return $this->getPathView($name, $required, $cache, $extension, 'forms', 'File:TplForm');
    }

    /** */
    public function getPathView($name, $required=true, $cache=false, $extension='.tpl', $subpath='views', $exceptionMsg='File:Tpl'){
        if($cache)
            $path = McCube_Bundle::getBundle('/application')
                ->subPaths['cache'].$this->name.DIRECTORY_SEPARATOR
            ;
        else
            $path = $this->subPaths[$subpath];

        $file = $path.$name.$extension;
        if($this->isFile($file, false)){
            return $file;
        }

        $file = $path.$name.'View'.$extension;
        if($this->isFile($file, false)){
            return $file;
        }

        if($required)
            throw new McCube_Bundle_Exception($exceptionMsg, 404, array(
                 'bundle' => $this->absoluteName
                ,'path'   => $path
                ,'name'   => $name
            ));

        return $path.$name.$extension;
    }

    /** */
    public function getForm($name, $viewPath=''){
        $partialFile = $viewPath.DIRECTORY_SEPARATOR.$name;
        if($config = $this->getConfigForm($partialFile)){
            $form = new McCube_Html_Form($name, $config, $this);
            $form->setTplPath($viewPath.DIRECTORY_SEPARATOR);
            return $this->register('form', $name, $form);
        }
    }

    /** */
    public function getController($name, $plugin=''){
        $name      = ucFirst($name);
        $file      = $this->subPaths['controllers'].$name;
        $classname = $plugin.$name.'Controller';
        if($this->isFileClass($file.'.php', $classname, false)){
            $object = new $classname($name, $this);
        }
        elseif($this->isFileClass($file.'Controller.php', $classname, false)){
            $object = new $classname($name, $this);
        }
        else {
            $object = new McCube_Controller($name, $this);
        }
        return $this->register('controller', $name, $object);
    }

    /** */
    public function getPlugin($name){
        $path = $this->subPaths['plugins'].$name.DIRECTORY_SEPARATOR;
        if(!$this->isPath($path, false))
            throw new McCube_Bundle_Exception('Plugin', 404, array(
                 'bundle' => $this->absoluteName
                ,'path'   => $path
                ,'name'   => $name
            ));

        return $this->register('plugin'
            , $name
            , new McCube_Bundle_Plugin($name, $path, $this)
        );
    }
}